package cn.daomeng.firebird.core.rsa;


import javax.crypto.Cipher;

import cn.daomeng.firebird.core.log.LogFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RSA{
	/**
	 * 加密算法RSA
	 */
	public static final String KEY_ALGORITHM = "RSA";
	/**
	 * 签名算法
	 */
	public static final String SIGNATURE_ALGORITHM = "SHA1WithRSA";
	//MD5withRSA
	/**
	 * RSA最大加密明文大小
	 */
	private static final int MAX_ENCRYPT_BLOCK = 117;
	/**
	 * RSA最大解密密文大小
	 */
	private static final int MAX_DECRYPT_BLOCK = 256;
	
	/**
	* RSA签名
	* @param content 待签名数据
	* @param privateKey 商户私钥
	* @param input_charset 编码格式
	* @return 签名值
	*/
	public static String sign(String content, String privateKey, String input_charset)
	{
        try 
        {
        	PKCS8EncodedKeySpec priPKCS8 	= new PKCS8EncodedKeySpec(Base64.decode(privateKey) ); 
        	KeyFactory keyf 				= KeyFactory.getInstance("RSA");
        	PrivateKey priKey 				= keyf.generatePrivate(priPKCS8);

            java.security.Signature signature = java.security.Signature
                .getInstance(SIGNATURE_ALGORITHM);

            signature.initSign(priKey);
            signature.update( content.getBytes(input_charset) );

            byte[] signed = signature.sign();
            
            return Base64.encode(signed);
        }
        catch (Exception e) 
        {
        	e.printStackTrace();
        }
        
        return null;
    }
	
	/**
	* RSA验签名检查
	* @param content 待签名数据
	* @param sign 签名值
	* @param publicKey 支付宝公钥
	* @param input_charset 编码格式
	* @return 布尔值
	*/
	public static boolean verify(String content, String sign, String publicKey, String input_charset)
	{
		try 
		{
			KeyFactory keyFactory = KeyFactory.getInstance(RSA.KEY_ALGORITHM);
	        byte[] encodedKey = Base64.decode(publicKey);
	        PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

		
			java.security.Signature signature = java.security.Signature
			.getInstance(SIGNATURE_ALGORITHM);
		
			signature.initVerify(pubKey);
			signature.update( content.getBytes(input_charset) );
		
			boolean bverify = signature.verify( Base64.decode(sign) );
			return bverify;
			
		} 
		catch (Exception e) 
		{
			//签名错误时，Base64.decode(sign)返回null,此处不需要打印异常，如果打印出来容易误导人
			//e.printStackTrace();
		}
		
		return false;
	}

	/**
	 *  通过私钥对原始值进行加密，返回加密串（Base64字符串）
	 *  <pre>
	 * 	    该方法需要配合decryptByPublicKey进行解密
	 * 	</pre>
	 * @param data 待签名数据
	 * @param privateKey 私钥值
	 * @return 加密串
	 */
	public static String encryptByPrivateKey(String data, String privateKey) throws Exception{
		byte[] bytes = encryptByPrivateKey(data.getBytes(),privateKey);
		return Base64.encode(bytes);
	}
	/**
	 *  通过公钥对原始值进行加密，返回加密串（Base64字符串）
	 *  <pre>
	 *      该方法需要配合decryptByPrivateKey进行解密
	 *  </pre>
	 * @param data 待签名数据
	 * @param publicKey 公钥值
	 * @return 加密串
	 */
	public static String encryptByPublicKey(String data , String publicKey) throws Exception{
		byte[] bytes = encryptByPublicKey(data.getBytes(),publicKey);
		return Base64.encode(bytes);
	}
	/**
	 *  通过私钥对加密值（Base64字符串）进行解密，返回原始值
	 *  <pre>
	 *      该方法的参数base64_data应是通过encryptByPublicKey进行解密串
	 *  </pre>
	 * @param base64_data 加密值（Base64字符串）
	 * @param privateKey 私钥值
	 * @return 加密串
	 */
	public static String decryptByPrivateKey(String base64_data, String privateKey) throws Exception {
		byte[] data = Base64.decode(base64_data);
		return new String(decryptByPrivateKey(data,privateKey));
	}
	/**
	 *  通过公钥对加密值（Base64字符串）进行解密，返回原始值
	 *  <pre>
	 *      该方法的参数base64_data应是通过encryptByPrivateKey进行解密串
	 *  </pre>
	 * @param base64_data 加密值（Base64字符串）
	 * @param publicKey 公钥值
	 * @return 加密串
	 */
	public static String decryptByPublicKey(String base64_data, String publicKey) throws Exception {
		byte[] data = Base64.decode(base64_data);
		return new String(decryptByPublicKey(data,publicKey));
	}

	/**
	 * <P>
	 * 私钥解密
	 * </p>
	 *
	 * @param encryptedData 已加密数据
	 * @param privateKey 私钥(BASE64编码)
	 * @return 解密内容
	 * @throws Exception 异常信息
	 */
	private static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
			throws Exception {
		byte[] keyBytes = Base64.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, privateK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
				cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return decryptedData;
	}

	/**
	 * <p>
	 * 公钥解密
	 * </p>
	 *
	 * @param encryptedData 已加密数据
	 * @param publicKey 公钥(BASE64编码)
	 * @return 公钥解密内容
	 * @throws Exception 异常信息
	 */
	private static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
			throws Exception {
		byte[] keyBytes = Base64.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key publicK = keyFactory.generatePublic(x509KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.DECRYPT_MODE, publicK);
		int inputLen = encryptedData.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段解密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
				cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_DECRYPT_BLOCK;
		}
		byte[] decryptedData = out.toByteArray();
		out.close();
		return decryptedData;
	}

	/**
	 * <p>
	 * 公钥加密
	 * </p>
	 *
	 * @param data 源数据
	 * @param publicKey 公钥(BASE64编码)
	 * @return 加密结果值
	 * @throws Exception 异常信息
	 */
	private static byte[] encryptByPublicKey(byte[] data, String publicKey)
			throws Exception {
		byte[] keyBytes = Base64.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key publicK = keyFactory.generatePublic(x509KeySpec);
		// 对数据加密
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, publicK);
		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段加密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
				cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_ENCRYPT_BLOCK;
		}
		byte[] encryptedData = out.toByteArray();
		out.close();
		return encryptedData;
	}

	/**
	 * <p>
	 * 私钥加密
	 * </p>
	 *
	 * @param data 源数据
	 * @param privateKey 私钥(BASE64编码)
	 * @return 加密结果值
	 * @throws Exception 异常信息
	 */
	private static byte[] encryptByPrivateKey(byte[] data, String privateKey)
			throws Exception {
		byte[] keyBytes = Base64.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
		cipher.init(Cipher.ENCRYPT_MODE, privateK);
		int inputLen = data.length;
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int offSet = 0;
		byte[] cache;
		int i = 0;
		// 对数据分段加密
		while (inputLen - offSet > 0) {
			if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
				cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
			} else {
				cache = cipher.doFinal(data, offSet, inputLen - offSet);
			}
			out.write(cache, 0, cache.length);
			i++;
			offSet = i * MAX_ENCRYPT_BLOCK;
		}
		byte[] encryptedData = out.toByteArray();
		out.close();
		return encryptedData;
	}
	
	/**
	* 得到私钥
	* @param key 密钥字符串（经过base64编码）
	* @throws Exception
	*/
	private static PrivateKey getPrivateKey(String key) throws Exception {

		byte[] keyBytes;
		
		keyBytes = Base64.decode(key);
		
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
		
		KeyFactory keyFactory = KeyFactory.getInstance(RSA.KEY_ALGORITHM);
		
		PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
		
		return privateKey;
	}
	
	 /** 
     * 通过公钥字符串得到公钥 
     *  
     * @param key 
     *            公钥字符串（经过base64编码） 
     * @throws Exception 
     */  
    private static PublicKey getPublicKey(String key) throws Exception {
        byte[] keyBytes = Base64.decode(key);  
  
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);  
  
        KeyFactory keyFactory = KeyFactory.getInstance(RSA.KEY_ALGORITHM);
  
        PublicKey publicKey = keyFactory.generatePublic(x509KeySpec);
        
        return publicKey;  
    }


	public static void main(String[] args) throws Exception{
		RSAKeys keysPair = RSASecrteKey.generateKeyPair();
		String privateKey = keysPair.getPrivateKey();
		String publicKey = keysPair.getPublicKey();
		System.out.println("privateKey : "+privateKey);
		System.out.println("publicKey : "+publicKey);

		String userId = "13300000001";
		int ttl = 300;
		Long start = System.currentTimeMillis();
		String content = userId+"_"+start+"_"+ttl;
		String sign = RSA.sign(content, privateKey,"utf-8");
		System.out.println("sign: "+ sign);
		System.out.println("verify : "+ RSA.verify(content,sign,publicKey,"utf-8"));



		String source = "这是一串原始内容这是一串原始内容这是";
		String sign1 = RSA.sign(source, privateKey,"utf-8");
		System.out.println("sign1: "+ sign1);
		System.out.println("verify1 : "+ RSA.verify(source,sign1,publicKey,"utf-8"));

		source = "公钥加密，私钥解密============================";
		byte[] data = source.getBytes();
		byte[] encodedData = encryptByPublicKey(data, publicKey);
		byte[] decodedData = decryptByPrivateKey(encodedData, privateKey);
		String target = new String(decodedData);
		System.out.println("解密结果 : "+ source.equals(target));


		source = "私钥加密，公钥解密++++++++++++++++++++++++++++";
		byte[] data1 = source.getBytes();
		byte[] encodedData1 = encryptByPrivateKey(data1, privateKey);
		byte[] decodedData1 = decryptByPublicKey(encodedData1, publicKey);
		String target1 = new String(decodedData1);
		System.out.println("解密结果 : "+ source.equals(target1));

	}
}
